package com.whisper.common.core.exception;

import com.whisper.common.core.domain.ExceptionResult;
import com.whisper.common.exception.ServiceException;
import com.whisper.common.exception.user.UserInnerAuthException;
import com.whisper.common.exception.user.UserNotPermissionException;
import com.whisper.common.exception.user.UserNotRoleException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.net.ConnectException;
import java.util.List;
import java.util.Objects;

/**
 * 
 * 异常处理
 * @author wangxiaoliang
 */
@RestControllerAdvice
public class GlobalExceptionAdvice {

    /**
     * 业务异常
     */
	@ResponseBody
    @ExceptionHandler(ServiceException.class)
    public ExceptionResult handleServiceException(ServiceException e, HttpServletRequest request) {
        Integer code = e.getCode();
        String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		return new ExceptionResult(e.getMessage(), Objects.isNull(code) ? HttpStatus.INTERNAL_SERVER_ERROR.value() : code, stackTrace, uri);
    }	
	
	@ResponseBody
	@ExceptionHandler(value = BindException.class)
	public ExceptionResult validationException(HttpServletRequest request, BindException e) {
		String uri = request.getRequestURI();
		String message = e.getAllErrors().get(0).getDefaultMessage();
		// 在这里判断是类型转换异常还是校验异常
		if (Objects.nonNull(message) && message.contains("Exception")) {
			FieldError objectError = (FieldError) e.getAllErrors().get(0);
			String field = objectError.getField();
			String fieldNew = field.replaceAll("[A-Z]", "_$0").toLowerCase();
			return new ExceptionResult(message, HttpStatus.BAD_REQUEST.value(), String.format("%s: 参数格式错误！", fieldNew), uri);
		}
		return new ExceptionResult(message, HttpStatus.BAD_REQUEST.value(), e.getMessage(), uri);
	}

	@ResponseBody
	@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
	public ExceptionResult validationNumException(HttpServletRequest request, MethodArgumentTypeMismatchException e) {
		String uri = request.getRequestURI();
		String fieldNew = e.getName().replaceAll("[A-Z]", "_$0").toLowerCase();
		return new ExceptionResult(HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value(), 
				String.format("%s: 参数格式错误！", fieldNew), uri);
	}

	@ResponseBody
	@ExceptionHandler(value = IllegalArgumentException.class)
	public ExceptionResult allegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		return new ExceptionResult("参数错误: " + e.getMessage(), HttpStatus.BAD_REQUEST.value(), stackTrace, uri);
	}

    /**
     * 请求方式不支持
     */
	@ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ExceptionResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
            HttpServletRequest request) {
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		return new ExceptionResult("请求类型不支持: " + e.getMessage(), HttpStatus.BAD_REQUEST.value(), stackTrace, uri);
    }	
	
	@ResponseBody
	@ExceptionHandler(value = ConnectException.class)
	public ExceptionResult connectException(HttpServletRequest request, ConnectException e) {
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.GATEWAY_TIMEOUT.value();

		return new ExceptionResult("无法连接到远程服务器。", code, stackTrace, uri);
	}

	@ResponseBody
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public ExceptionResult methodArgumentNotValidException(HttpServletRequest request,
			MethodArgumentNotValidException e) {
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		List<ObjectError> errors = e.getBindingResult().getAllErrors();
		StringBuffer errorMsg = new StringBuffer();
		errors.stream().forEach(x -> errorMsg.append(x.getDefaultMessage()).append(";"));
		String message = errorMsg.toString();
		Integer code = HttpStatus.BAD_REQUEST.value();

		return new ExceptionResult(message, code, stackTrace, uri);
	}

	@ResponseBody
	@ExceptionHandler(value = NoHandlerFoundException.class)
	public ExceptionResult noHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.NOT_FOUND.value();

		return new ExceptionResult("无此路由！", code, stackTrace, uri);
	}

	/**
     * 权限码异常
     */
	@ResponseBody
    @ExceptionHandler(UserNotPermissionException.class)
    public ExceptionResult handleNotPermissionException(UserNotPermissionException e, HttpServletRequest request) {
        String uri = request.getRequestURI();
        String stackTrace = Throwabler.getFirstStackInfo(e);
        return new ExceptionResult("没有访问权限，请联系管理员授权", HttpStatus.FORBIDDEN.value(), stackTrace, uri);
    }	
	
	/**
     * 角色权限异常
     */
	@ResponseBody
    @ExceptionHandler(UserNotRoleException.class)
    public ExceptionResult handleNotRoleException(UserNotRoleException e, HttpServletRequest request) {
		String uri = request.getRequestURI();
        String stackTrace = Throwabler.getFirstStackInfo(e);
        return new ExceptionResult("没有访问权限，请联系管理员授权", HttpStatus.FORBIDDEN.value(), stackTrace, uri);
    }
	
    /**
     * 内部认证异常
     */
	@ResponseBody
    @ExceptionHandler(UserInnerAuthException.class)
    public ExceptionResult handleInnerAuthException(UserInnerAuthException e, HttpServletRequest request) {
		String uri = request.getRequestURI();
        String stackTrace = Throwabler.getFirstStackInfo(e);
        return new ExceptionResult("认证不通过，请联系管理员", HttpStatus.FORBIDDEN.value(), stackTrace, uri);
    }
	
	@ResponseBody
	@ExceptionHandler(Exception.class)
	public ExceptionResult defaultException(HttpServletRequest request, Exception e) {
		e.printStackTrace();
		String uri = request.getRequestURI();
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.INTERNAL_SERVER_ERROR.value();
        return new ExceptionResult(stackTrace.contains("SQL") ? HttpStatus.INTERNAL_SERVER_ERROR.name() : e.getMessage(), code, stackTrace, uri);
	}

}
